<template>
  <view
    class="QS-tabs"
    :style="{
      'z-index': zIndex,
      'font-size': getFontSize + 'rpx',
      'background-color': getBgColor,
      'transition-duration': getDuration + 's'
    }"
  >
    <scroll-view
      scroll-x
      class="QS-tabs-scroll"
      :scroll-left="left"
      scroll-with-animation
      :style="{
        'z-index': Number(zIndex) + 1
      }"
    >
      <view class="QS-tabs-scroll-box">
        <!-- 循环tabs -->
        <view
          v-for="(item, index) in getTabs"
          :id="preId + index"
          :key="index"
          class="QS-tabs-scroll-item"
          :style="{
            height: getHeight + 'rpx',
            'line-height': getHeight + 'rpx',
            'min-width': getWidth + 'rpx',
            padding: '0 ' + space + 'rpx',
            color: index === getCurrent ? getActiveColor : getDefaultColor,
            'font-weight': activeBold && index === getCurrent ? 'bold' : '',
            'transition-duration': getDuration + 's',
            'z-index': Number(zIndex) + 2
          }"
          @tap="emit(index)"
        >
          <!-- line1 -->
          <view
            v-if="animationMode === 'line1'"
            class="boxStyle"
            :style="
              getDurationStyle +
                (index === getCurrent ? getActiveStyle : getDefaultStyle)
            "
          />
          {{ item.name || item }}
        </view>
        <!-- itemBackground -->
        <view
          v-if="hasItemBackground"
          class="itemBackgroundBox"
          :style="{
            height: getHeight + 'rpx',
            width:
              (isLine3 && tabsInfo[animationFinishCurrent]
                ? tabsInfo[animationFinishCurrent].width
                : tabsInfo[getCurrent].width) + 'px',
            'z-index': Number(zIndex) + 1,
            'transition-duration': getDuration + 's',
            left: (tabsInfo[getCurrent] ? tabsInfo[getCurrent].left : 0) + 'px'
          }"
        >
          <view
            class="itemBackground"
            :style="
              'transition-duration:' +
                getDuration +
                's;' +
                'background-color:' +
                getItemBackgroundColor +
                ';' +
                'box-shadow: 0 0 5rpx 5rpx ' +
                getItemBackgroundColor +
                ';' +
                itemBackgroundStyle +
                ';'
            "
          />
        </view>
        <!-- line2 -->
        <view
          v-if="animationMode === 'line2'"
          class="boxStyle2"
          :style="
            getLinezIndex +
              getDurationStyle +
              'width:' +
              lW +
              'px;' +
              'background-color:' +
              (lineColor || getActiveColor) +
              ';' +
              line2Style +
              ';' +
              'left:' +
              line2Dx +
              'px;'
          "
        />

        <view
          v-if="animationMode === 'line3'"
          class="boxStyle2"
          :style="
            getLinezIndex +
              'width:' +
              lW +
              'px;' +
              'background-color:' +
              (lineColor || getActiveColor) +
              ';' +
              line2Style +
              ';' +
              'left:' +
              getLine3Dx +
              'px'
          "
        />
      </view>
    </scroll-view>
  </view>
</template>

<script>
const { windowWidth } = uni.getSystemInfoSync();
const preId = "QSTabsID_";
import { subjectInfo } from "@/settings";
export default {
  props: {
    tabs: {
      // 需循环的标签列表
      type: Array,
      default() {
        return [];
      }
    },
    current: {
      // 当前所在滑块的 index
      type: Number,
      default: 0
    },
    height: {
      // QS-tabs的高度和行高
      type: [String, Number],
      default: 80
    },
    minWidth: {
      // 单个tab的最小宽度	//v1.4修改
      type: [String, Number],
      default: 250
    },
    fontSize: {
      // 字体大小
      type: [String, Number],
      default: 30
    },
    duration: {
      // 过渡动画时长, 单位 s
      type: [String, Number],
      default: 0.5
    },
    activeColor: {
      // 选中项的主题颜色
      type: String,
      default: subjectInfo.theme
    },
    defaultColor: {
      // 未选中项的主题颜色
      type: String,
      default: "#707070"
    },
    animationLineWidth: {
      // 动画线条的宽度
      type: [String, Number],
      default: 10
    },
    line2Style: {
      // line2线条的样式
      type: String,
      default: "height: 8rpx;border-radius: 4rpx;"
    },
    animationMode: {
      // 动画类型
      type: String,
      default: "line1"
    },
    autoCenter: {
      // 是否自动滚动至中心目标
      type: Boolean,
      default: true
    },
    autoCenterMode: {
      // 滚动至中心目标类型
      type: String,
      default: "component"
    },
    activeStyle: {
      // line1模式选中项的样式
      type: String,
      default:
        "bottom:0;left:50%;transform: translate(-50%,-100%);height: 8rpx;border-radius:4rpx;"
    },
    defaultStyle: {
      // line1模式未选中项的样式
      type: String,
      default:
        "bottom:0;left:50%;transform: translate(-50%,-100%);height: 8rpx;border-radius:4rpx;"
    },
    backgroundColor: {
      // 统一背景颜色
      type: String,
      default: "rgba(255,255,255,1.0)"
    },
    hasItemBackground: {
      // 是否开启背景追光
      type: Boolean,
      default: false
    },
    itemBackgroundColor: {
      // 统一追光背景颜色
      type: String,
      default: "rgba(255,255,255,1.0)"
    },
    itemBackgroundStyle: {
      // 追光样式
      type: String,
      default: ""
    },
    zIndex: {
      // css的z-index属性值
      type: [String, Number],
      default: 99
    },
    swiperWidth: {
      // line3生效, 外部swiper的宽度, 单位rpx
      type: [String, Number],
      default: 750
    },
    space: {
      // tab间距
      type: [String, Number],
      default: "40"
    },
    activeBold: {
      // 当前tab字体是否加粗
      type: Boolean,
      default: false
    },
    lineColor: {
      // line颜色
      type: String,
      default: ""
    }
  },
  data() {
    return {
      left: 0,
      tabsInfo: [],
      line2Width: Number(this.animationLineWidth),
      setTimeoutFc: null,
      componentsWidth: 0,
      animationFinishCurrent: this.current,
      pxWidth: 0,
      lW: 0,
      sW: 0,
      preId,
      line3Dx: 0,
      line3AddDx: 0,
      line2Dx: 0
    };
  },
  computed: {
    isLine3() {
      return this.animationMode === "line3";
    },
    getCurrent() {
      const current = Number(this.current);
      if (current > this.getTabs.length - 1) {
        return this.getTabs.length - 1;
      }
      return current;
    },
    getTabs() {
      return this.tabs;
    },
    getHeight() {
      return Number(this.height);
    },
    getWidth() {
      return Number(this.minWidth);
    },
    getFontSize() {
      return this.fontSize;
    },
    getDuration() {
      return Number(this.duration);
    },
    getBgColor() {
      const defaultColor = this.backgroundColor || "rgba(255,255,255,0)";
      if (this.getTabs[this.getCurrent] instanceof Object) {
        return this.getTabs[this.getCurrent].backgroundColor || defaultColor;
      } else {
        return defaultColor;
      }
    },
    getItemBackgroundColor() {
      const defaultColor = this.itemBackgroundColor || "rgba(255,255,255,0)";
      if (this.getTabs[this.getCurrent] instanceof Object) {
        return (
          this.getTabs[this.getCurrent].itemBackgroundColor || defaultColor
        );
      } else {
        return defaultColor;
      }
    },
    getDurationStyle() {
      return `transition-duration: ${this.getDuration}s;`;
    },
    getActiveColor() {
      let activeColor;
      if (this.getTabs[this.getCurrent] instanceof Object) {
        if (this.getTabs[this.getCurrent].activeColor) {
          activeColor = this.getTabs[this.getCurrent].activeColor;
        } else {
          activeColor = this.activeColor;
        }
      } else {
        activeColor = this.activeColor;
      }
      return activeColor;
    },
    getDefaultColor() {
      let defaultColor;
      if (this.getTabs[this.getCurrent] instanceof Object) {
        if (this.getTabs[this.getCurrent].defaultColor) {
          defaultColor = this.getTabs[this.getCurrent].defaultColor;
        } else {
          defaultColor = this.defaultColor;
        }
      } else {
        defaultColor = this.defaultColor;
      }
      return defaultColor;
    },
    getActiveStyle() {
      return `width:${this.animationLineWidth}%;background-color:${this.getActiveColor};${this.activeStyle};`;
    },
    getDefaultStyle() {
      return `width:0;background-color:${this.getActiveColor};${this.defaultStyle};`;
    },
    getLinezIndexNum() {
      return Number(this.zIndex) + 2;
    },
    getLinezIndex() {
      return `z-index: ${this.getLinezIndexNum};`;
    },
    getLine3Dx() {
      return Number(this.line3Dx) + Number(this.line3AddDx);
    }
  },
  watch: {
    current(n, o) {
      this.change(n);
    },
    tabs() {
      this.init();
    }
  },
  // #ifndef H5
  onReady() {
    this.init();
  },
  // #endif
  // #ifdef H5
  mounted() {
    this.init();
  },
  // #endif
  methods: {
    init() {
      this.countPx();
      const view = uni.createSelectorQuery().in(this);
      for (let i = 0; i < this.tabs.length; i++) {
        view.select("#" + preId + i).boundingClientRect();
      }
      view.exec(res => {
        const arr = [];
        for (let i = 0; i < res.length; i++) {
          arr.push(res[i]);
        }
        this.tabsInfo = arr;
        this.countLine2Dx();
        this.countLine3Dx();
        const _this = this;
        _this.getQuery(() => {
          _this.countScrollX();
        });
      });
    },
    countLine2Dx() {
      if (this.animationMode === "line2") {
        const tab = this.tabsInfo[this.getCurrent];
        if (tab) this.line2Dx = tab.left + tab.width / 2 - this.lW / 2;
      }
    },
    countLine3Dx() {
      if (this.animationMode === "line3") {
        const tab = this.tabsInfo[this.animationFinishCurrent];
        if (tab) this.line3Dx = tab.left + tab.width / 2 - this.lW / 2;
      }
    },
    countPx() {
      const w = uni.upx2px(this.getWidth);
      this.pxWidth = w;
      this.lW = w * (Number(this.animationLineWidth) / 100);
      this.sW = uni.upx2px(Number(this.swiperWidth));
    },
    emit(index) {
      this.$emit("change", index);
    },
    change() {
      this.countScrollX();
      if (this.animationMode === "line2") {
        this.line2Width = 2;
        if (this.setTimeoutFc) clearTimeout(this.setTimeoutFc);
        this.setTimeoutFc = setTimeout(() => {
          this.line2Width = this.animationLineWidth;
        }, (this.getDuration * 1000 * 3) / 5);
        this.countLine2Dx();
      }
    },
    getQuery(cb) {
      try {
        const view = uni
          .createSelectorQuery()
          .in(this)
          .select(".QS-tabs");
        view
          .fields(
            {
              size: true
            },
            data => {
              if (data) {
                this.componentsWidth = data.width;
                if (cb && typeof cb === "function") cb(data);
              } else {
                this.retryGetQuery(cb);
              }
            }
          )
          .exec();
      } catch (e) {
        // TODO handle the exception
        this.componentsWidth = windowWidth;
      }
    },
    retryGetQuery(cb) {
      try {
        const view = uni.createSelectorQuery().select(".QS-tabs");
        view
          .fields(
            {
              size: true
            },
            data => {
              if (data) {
                this.componentsWidth = data.width;
              } else {
                this.componentsWidth = windowWidth;
              }
              if (cb && typeof cb === "function") cb(data);
            }
          )
          .exec();
      } catch (e) {
        // TODO handle the exception
        this.componentsWidth = windowWidth;
      }
    },
    countScrollX() {
      if (this.autoCenter) {
        let tab;
        if (this.isLine3) {
          tab = this.tabsInfo[this.animationFinishCurrent];
        } else {
          tab = this.tabsInfo[this.getCurrent];
        }
        if (tab) {
          const tabCenter = tab.left + tab.width / 2;
          let fatherWidth;
          if (this.autoCenterMode === "window") {
            fatherWidth = windowWidth;
          } else {
            fatherWidth = this.componentsWidth;
          }
          this.left = tabCenter - fatherWidth / 2;
        }
      }
    },
    setDx(dx) {
      const tab = this.tabsInfo[
        dx > 0
          ? this.animationFinishCurrent + 1
          : this.animationFinishCurrent - 1
      ];
      this.line3AddDx = (dx / this.sW) * (tab ? tab.width : this.pxWidth);
    },
    setFinishCurrent(current) {
      this.line3AddDx = 0;
      this.animationFinishCurrent = current;
      this.countLine3Dx();
    }
  }
};
</script>

<style scoped>
view,
scroll-view {
  box-sizing: border-box;
}

.QS-tabs {
  width: 100%;
  transition-property: background-color, color;
}

.QS-tabs::-webkit-scrollbar {
  display: none;
  width: 0 !important;
  height: 0 !important;
  -webkit-appearance: none;
  background: transparent;
}

.QS-tabs-scroll {
  width: 100%;
  white-space: nowrap;
  position: relative;
}

.QS-tabs-scroll-box {
  position: relative;
  display: flex;
  white-space: nowrap !important;
  display: block !important;
}

.QS-tabs-scroll-item {
  position: relative;
  display: inline-block;
  text-align: center;
  transition-property: background-color, color, font-weight;
  background: rgba(216, 216, 216, 0);
}

.content {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.boxStyle {
  pointer-events: none;
  position: absolute;
  transition-property: all;
}

.boxStyle2 {
  pointer-events: none;
  position: absolute;
  bottom: 0;
  transition-property: all;
  transform: translateY(-100%);
}

.itemBackgroundBox {
  pointer-events: none;
  position: absolute;
  top: 0;
  transition-property: left, background-color;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}

.itemBackground {
  height: 100%;
  width: 100%;
  transition-property: all;
}
</style>
